﻿using System;
using System.Collections;
using System.Linq;

namespace NumberParser
{
    public static class RequireArgumentExtensions
    {
        public static Require.RequireArgument<int> IsInRange(this Require.RequireArgument<int> @this, int inclusiveMinimumValue, int exclusiveMinimumValue)
        {
            @this.Validate();
            
            if ((@this.Value < inclusiveMinimumValue) || (@this.Value >= exclusiveMinimumValue))
            {
                throw new ArgumentOutOfRangeException(@this.Name);
            }

            return @this;
        }

        public static Require.RequireArgument<T> IsNotEmpty<T>(this Require.RequireArgument<T> @this)
            where T : IEnumerable
        {
            @this.Validate();

            if (!@this.Value.OfType<object>().Any())
            {
                throw new ArgumentException("Cannot be empty.", @this.Name);
            }

            return @this;
        }

        public static Require.RequireArgument<Type> IsAssignableTo(this Require.RequireArgument<Type> @this, Type type)
        {
            @this.Validate();

            if (!type.IsAssignableFrom(@this.Value))
            {
                throw new ArgumentException(string.Format("Must be assignable to '{0}'.", type), @this.Name);
            }

            return @this;
        }

        public static Require.RequireArgument<T> IsNotNull<T>(this Require.RequireArgument<T> @this)
            where T : class
        {
            @this.Validate();

            if (@this.Value == null)
            {
                throw new ArgumentNullException(@this.Name);
            }

            return @this;
        }

        public static Require.RequireArgument<T> IsNotNullOrEmpty<T>(this Require.RequireArgument<T> @this)
            where T : class, IEnumerable
        {
            return @this.IsNotNull().IsNotEmpty();
        }

        public static Require.RequireArgument<char> IsLetter(this Require.RequireArgument<char> @this)
        {
            @this.Validate();

            if (!char.IsLetter(@this.Value))
            {
                throw new ArgumentException("Must be a letter.");
            }

            return @this;
        }

        public static Require.RequireArgument<char> IsDigit(this Require.RequireArgument<char> @this)
        {
            @this.Validate();

            if (!char.IsDigit(@this.Value))
            {
                throw new ArgumentException("Must be a digit.");
            }

            return @this;
        }

        public static Require.RequireArgument<char> IsLetterOrDigit(this Require.RequireArgument<char> @this)
        {
            @this.Validate();

            if (!char.IsLetterOrDigit(@this.Value))
            {
                throw new ArgumentException("Must be a letter or digit.");
            }

            return @this;
        }

        public static Require.RequireArgument<T> IsValid<T>(this Require.RequireArgument<T> @this, Func<T, bool> predicate, string message = "Argument is invalid.")
        {
            @this.Validate();

            if (!predicate(@this.Value))
            {
                throw new ArgumentException(message, @this.Name);
            }

            return @this;
        }

        private static void Validate<T>(this Require.RequireArgument<T> @this)
        {
            if (@this == null)
            {
                throw new ArgumentNullException("this");
            }
        }
    }
}